package references

import (
	"fmt"
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 调用普通的实例方法，相当于C++里面的虚方法。实例方法可能会被子类重写
type INVOKE_VIRTUAL struct{ base.Index16Instruction }

func (self *INVOKE_VIRTUAL) Execute(frame *runtime_area.Frame) {
	currentClass := frame.Method().Class()
	constantPool := currentClass.ConstantPool()
	methodRef := constantPool.GetConstant(self.Index).(*heap.MethodRef)

	method := methodRef.ResolveMethod()
	// 这个指令不能用来调用静态方法
	if method.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	descriptor := method.Descriptor()

	// 要调用这个方法的实例
	object := frame.OperandStack().GetRefFromTop(method.ArgsSlotCount())
	if object == nil {
		// todo 暂时还没实现直接将数据打印到标准输出，先借用go语言的Println方法
		if method.Name() == "println" {
			switch descriptor {
			case "(Ljava/lang/String;)V":
				// 弹出参数
				java_String := frame.OperandStack().PopRef()
				go_string := GoString(java_String)
				fmt.Println(go_string)
				frame.OperandStack().PopRef()// 弹出out对象
			default:
				// 弹出参数
				fmt.Println(frame.OperandStack().PopInt())
				frame.OperandStack().PopRef()// 弹出out对象
			}
			return
		}
		panic("java.lang.NullPointerException")
	}
	// 检查是否有权限访问
	if !base.CheckMethodAccess(method, object, currentClass) {
		panic("java.lang.IllegalAccessError")
	}

	// 根据object对象实际所属的类，决定应该调用哪个方法
	methodToBeInvoke := heap.LookupMethodInClass(object.Class(),methodRef.Name(),methodRef.Descriptor())

	if methodToBeInvoke == nil ||methodToBeInvoke.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}
	base.InvokeMethod(frame,methodToBeInvoke)
}

func GoString(java_string *heap.Object) string {
	charArray := java_string.GetRefVar("value","[C");
	chars := charArray.Chars()
	return heap.Utf16ToString(chars)
}
